﻿////TODO: rewrite this class and make it more tidy, compact, readable
//using System;
//using System.Collections.Generic;
//using System.Text;
//using System.Threading;
//using System.IO;
//using System.Runtime.Serialization.Formatters.Binary;

//namespace DCRF.Common.Distribution
//{
//    public delegate void MessageHandlerDelegate(PeerSocket source, string msgCode, Guid msgId, string senderId, string receiverId, List<object> data);

//    public class PeerSocket : IDisposable
//    {
//        #region private fields
//        public static Random random = new Random();

//        public const string ConnectMsgCode = "__CONNECT__";
//        public const string DisconnectMsgCode = "__DISCONNECT__";
//        public const string DefaultMsgCode = "*";  //if a message is received that has no handler, call handler of this message-code
//        //private const int defaultMiniWait = 0;
//        //private const int defaultReceiveInterval = 100;
//        //private const int defaultWait = 1000;  //wait at the end of connect/disconnect to ensure everything is done on the other side  
//        private const int defaultTimeout = 5000;
//        public const string ResponseSuffix = ".RESP";

//        private BinaryFormatter formatter = new BinaryFormatter();
//        private ISimpleSocket serverSocket = null;
//        private ISimpleSocket clientSocket = null;  //req does not need a thread, we wait for ACK after sending messages in the method
//        private int port = -1;
//        //private Thread repThread = null;

//        //key can be msgCode or msgId
//        private Dictionary<object, MessageHandlerDelegate> handlers = new Dictionary<object, MessageHandlerDelegate>();
//        private string parentId = "";
//        private string peerId = "";

//        private bool isDisposed = false;
//        //private bool isReceiving = false;

//        private object reqLock = new object();
//        #endregion

//        #region private methods

//        private void handleConnectRequest(PeerSocket sender, string msgCode, Guid msgId, string senderId, string receiverId, List<object> data)
//        {
//            string repAddress = (string)data[0];

//            //connect to that address
//            clientSocket = new SimpleSocket(false, this.owner);
//            clientSocket.Connect(repAddress);
//        }

//        private void handleDisconnectRequest(PeerSocket sender, string msgCode, Guid msgId, string senderId, string receiverId, List<object> data)
//        {
//            Dispose();
//        }

//        //private void repReceiveHandler()
//        //{
//        //    while (!isDisposed)
//        //    {
//        //        //important messages are received here
//        //        //only ACK is sent here(send ACK ASAP)
//        //        byte[] data = serverSocket.Recv(defaultMiniWait);

//        //        if (data == null || data.Length == 0)
//        //        {
//        //            Thread.Sleep(defaultReceiveInterval);

//        //            continue;
//        //        }

//        //        //send empty ACK message
//        //        serverSocket.Send(null);

//        //        MemoryStream ms = new MemoryStream(data);

//        //        List<object> msg = formatter.Deserialize(ms) as List<object>;

//        //        ThreadPool.QueueUserWorkItem(new WaitCallback(processMessage), msg);
//        //    }

//        //    //when thread is finished, release data
//        //    //sometimes repSocket.Dispose hangs! so we just set it to null, it will be disposed by GC (we assume :) )
//        //    //repSocket.Dispose();
//        //    serverSocket = null;
//        //}

//        private void processMessage(object data)
//        {
//            List<object> msg = data as List<object>;
//            Guid msgId = (Guid)msg[0];
//            string code = (string)msg[1];
//            string senderId = (string)msg[2];
//            string receiverId = (string)msg[3];

//            msg.RemoveAt(0);
//            msg.RemoveAt(0);
//            msg.RemoveAt(0);
//            msg.RemoveAt(0);

//            //do we have a handler for this type of message?
//            if (handlers.ContainsKey(code) || handlers.ContainsKey(DefaultMsgCode) || handlers.ContainsKey(msgId))
//            {
//                //am I the receiver of this message?
//                if (receiverId == null || receiverId == this.parentId)
//                {
//                    //is my peer sender of the message
//                    if (this.peerId == null || senderId == this.peerId)
//                    {
//                        if (handlers.ContainsKey(msgId))
//                        {
//                            handlers[msgId](this, code, msgId, senderId, receiverId, msg);
//                        }
//                        else if (handlers.ContainsKey(code))
//                        {
//                            handlers[code](this, code, msgId, senderId, receiverId, msg);
//                        }
//                        else //if there is no handler defined for this code/id call default handler
//                        {
//                            handlers[DefaultMsgCode](this, code, msgId, senderId, receiverId, msg);
//                        }
//                    }
//                }
//            }
//        }

//        #endregion

//        #region public methods
//        public object owner = null;

//        public PeerSocket(object owner)
//            : this("", "", owner)
//        {
//        }

//        public PeerSocket(string _parentId, string _peerId, object owner)
//        {
//            port = random.Next(1025, 65500);
//            parentId = _parentId;
//            peerId = _peerId;
//            this.owner = owner;

//            serverSocket = new SimpleSocket(true, owner);
//            serverSocket.Bind(Address);
//            serverSocket.DataReceived += new OnDataReceivedDelegate(serverSocket_DataReceived);

//            //repThread = new Thread(new ThreadStart(repReceiveHandler));
//            //repThread.Name = "SimpleSocket.REP." + port.ToString();
//            //repThread.Start();

//            SetHandler(ConnectMsgCode, new MessageHandlerDelegate(handleConnectRequest));
//            SetHandler(DisconnectMsgCode, new MessageHandlerDelegate(handleDisconnectRequest));

//            //we do not start req here as we are not connected to any address
//        }

        

//        void serverSocket_DataReceived(ISimpleSocket sender, byte[] data)
//        {
//            //send empty ACK message
//            serverSocket.Send(new byte[0]);

//            MemoryStream ms = new MemoryStream(data);
//            List<object> msg = formatter.Deserialize(ms) as List<object>;
//            ThreadPool.QueueUserWorkItem(new WaitCallback(processMessage), msg);
//        }

//        public void Connect(string address)
//        {
//            if (isDisposed) return;

//            //it is possible to call Connect multiple times, in some rare instances
//            if (clientSocket != null)
//            {
//                clientSocket.Dispose();
//            }

//            clientSocket = new SimpleSocket(false, owner);
//            clientSocket.Connect(address);
//            clientSocket.DataReceived += new OnDataReceivedDelegate(clientSocket_DataReceived);

//            //inform other end that we are connected and tell your rep address
//            //id is not used in this class but in another class that uses this class
//            SendMessageAsync(ConnectMsgCode, Guid.Empty, this.Address);

//            //wait to ensure that message is received and processed at the other side
//            Thread.Sleep(1000);
//        }

//        void clientSocket_DataReceived(ISimpleSocket sender, byte[] data)
//        {
//            //Do nothing, a simple byte[]{0} is received here as ACK
//        }

//        public void Disconnect()
//        {
//            if (isDisposed) return;

//            SendMessageAsync(DisconnectMsgCode, Guid.Empty);

//            //just to be sure everything is sent
//            //Thread.Sleep(defaultWait);

//            Dispose();
//        }

//        public void SendMessageAsync(string msgCode, Guid msgId, params object[] items)
//        {
//            List<object> list = new List<object>(items);

//            SendMessageAsync(msgCode, msgId, list);
//        }

//        public void SendMessageAsync(string msgCode, Guid msgId, List<object> list)
//        {
//            if (clientSocket == null || isDisposed)
//            {
//                //maybe this socket is disposed before this call
//                //so we just ignore the call
//                return;
//            }

//            list.Insert(0, peerId);
//            list.Insert(0, parentId);
//            list.Insert(0, msgCode);
//            list.Insert(0, msgId);

//            MemoryStream ms = new MemoryStream();
//            formatter.Serialize(ms, list);

//            //if recv is being executed in another thread, wait for it to finish
//            lock (reqLock)
//            {
//                if (clientSocket == null)
//                {
//                    //maybe socket is disposed 
//                    dispose();
//                    return;
//                }

//                clientSocket.Send(ms.GetBuffer());

//                //isReceiving = true;
//                //int counter = 0;

//                ////wait for ACK
//                //byte[] dummy = clientSocket.Recv(defaultMiniWait);

//                //while (dummy == null)
//                //{
//                //    Thread.Sleep(defaultReceiveInterval); 
//                //    dummy = clientSocket.Recv(defaultMiniWait);

//                //    counter += defaultMiniWait;
//                //    counter += defaultReceiveInterval;

//                //    if (counter > defaultTimeout)
//                //    {
//                //        //something has happened! we are waiting for an empty response for 5secs and
//                //        //nothing is received

//                //        //isReceiving = false;
//                //        dispose();
//                //        dummy = new byte[0];  //force end the loop
//                //        continue;
//                //    }
//                //}
//            }
//        }

//        public void SendResponse(string msgCode, Guid msgId, params object[] items)
//        {
//            SendResponse(msgCode, msgId, new List<object>(items));
//        }

//        public void SendResponse(string msgCode, Guid msgId, List<object> result)
//        {
//            //respnse is sent to the sender not to all peers
//            SendMessageAsync(msgCode + ResponseSuffix, msgId, result);
//        }

//        public void SetHandler(object msgCodeOrId, MessageHandlerDelegate handler)
//        {
//            handlers[msgCodeOrId] = handler;
//        }

//        public void ResetHandler(object msgCodeOrId)
//        {
//            handlers.Remove(msgCodeOrId);
//        }

//        private void dispose()
//        {
//            if (clientSocket != null)
//            {
//                clientSocket.Dispose();
//            }

//            if (serverSocket != null)
//            {
//                serverSocket.Dispose();
//            }

//            clientSocket = null;
//            serverSocket = null;

//            //rep socket cannot be manipulated directly, finish its thread (by setting isDisposed to true) and it will handle dispose
//            isDisposed = true;
//        }

//        public void Dispose()
//        {
//            if (isDisposed) return;

//            //if client is sending some data, wait for finish
//            lock (reqLock)
//            {
//                dispose();
//            }
//        }

//        #endregion

//        #region public properties

//        public bool IsDisposed
//        {
//            get
//            {
//                return isDisposed;
//            }
//        }


//        public string Address
//        {
//            get
//            {
//                return string.Format("127.0.0.1:{0}", port);
//            }
//        }

//        #endregion
//    }
//}
